﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace POVRay {
    public struct Color {
        public float R, G, B, A;

        public Color(float r, float g, float b) {
            this.R = r;
            this.G = g;
            this.B = b;
            this.A = 1.0f;
        }

        public Color(float r, float g, float b, float a) {
            this.R = r;
            this.G = g;
            this.B = b;
            this.A = a;
        }

        public static float[] RGBComponentsFromHSB(float hue, float saturation, float brightness) { //adopted from java.awt: Color.java
            float r = 0, g = 0, b = 0;
            if (saturation == 0) {
                r = g = b = brightness;
            } else {
                float h = (hue - (float)Math.Floor(hue)) * 6.0f;
                float f = h - (float)Math.Floor(h);
                float p = brightness * (1.0f - saturation);
                float q = brightness * (1.0f - saturation * f);
                float t = brightness * (1.0f - (saturation * (1.0f - f)));
                switch ((int)h) {
                    case 0:
                        r = brightness;
                        g = t;
                        b = p;
                        break;
                    case 1:
                        r = q;
                        g = brightness;
                        b = p;
                        break;
                    case 2:
                        r = p;
                        g = brightness;
                        b = t;
                        break;
                    case 3:
                        r = p;
                        g = q;
                        b = brightness;
                        break;
                    case 4:
                        r = t;
                        g = p;
                        b = brightness;
                        break;
                    case 5:
                        r = brightness;
                        g = p;
                        b = q;
                        break;
                }
            }
            return new float[]{r, g, b};
        }

        public static Color FromHSB(float hue, float saturation, float brightness) {
            return Color.FromHSB(hue, saturation, brightness, 1.0f);
        }

        public static Color FromHSB(float hue, float saturation, float brightness, float alpha) {
            float[] rgb = Color.RGBComponentsFromHSB(hue, saturation, brightness);
            return new Color(rgb[0], rgb[1], rgb[2], alpha);
        }

        public float Hue {
            get {
                return HSBComponents()[0];
            }
            set {
                float[] hsb = HSBComponents();
                hsb[0] = value;
                float[] rgb = Color.RGBComponentsFromHSB(hsb[0], hsb[1], hsb[2]);
                R = rgb[0];
                G = rgb[1];
                B = rgb[2];
            }
        }

        public float Saturation {
            get {
                return HSBComponents()[1];
            }
            set {
                float[] hsb = HSBComponents();
                hsb[1] = value;
                float[] rgb = Color.RGBComponentsFromHSB(hsb[0], hsb[1], hsb[2]);
                R = rgb[0];
                G = rgb[1];
                B = rgb[2];
            }
        }

        public float Brightness {
            get {
                return HSBComponents()[2];
            }
            set {
                float[] hsb = HSBComponents();
                hsb[2] = value;
                float[] rgb = Color.RGBComponentsFromHSB(hsb[0], hsb[1], hsb[2]);
                R = rgb[0];
                G = rgb[1];
                B = rgb[2];
            }
        }

        public float[] HSBComponents() { //adopted from java.awt: Color.java
            float r = R;
            float g = G;
            float b = B;
            float[] hsbvals = new float[3];
            float hue, saturation, brightness;
            float cmax = (r > g) ? r : g;
            if (b > cmax) cmax = b;
            float cmin = (r < g) ? r : g;
            if (b < cmin) cmin = b;

            brightness = cmax;
            if (cmax != 0)
                saturation = (cmax - cmin) / cmax;
            else
                saturation = 0;
            if (saturation == 0)
                hue = 0;
            else {
                float redc = (cmax - r) / (cmax - cmin);
                float greenc = (cmax - g) / (cmax - cmin);
                float bluec = (cmax - b) / (cmax - cmin);
                if (r == cmax)
                    hue = bluec - greenc;
                else if (g == cmax)
                    hue = 2.0f + redc - bluec;
                else
                    hue = 4.0f + greenc - redc;
                hue = hue / 6.0f;
                if (hue < 0)
                    hue = hue + 1.0f;
            }
            hsbvals[0] = hue;
            hsbvals[1] = saturation;
            hsbvals[2] = brightness;
            return hsbvals;
        }

        public static implicit operator Color(float white) {
            return new Color(white, white, white, white);
        }

        public void InvertRGB() {
            R = 1.0f - R;
            G = 1.0f - G;
            B = 1.0f - B;
        }

        public Color InvertedRGB() {
            return new Color(1.0f - R, 1.0f - G, 1.0f - B, A);
        }

        public override string ToString() {
            if(R == G && G == B && B == A) {
                return R.POVRayString();
            } else {
                //return "color rgbf <" + R.POVRayString() + ", " + G.POVRayString() + ", " + B.POVRayString() + ", " + (1.0f - A).POVRayString() + ">";
                return "rgbf <" + R.POVRayString() + ", " + G.POVRayString() + ", " + B.POVRayString() + ", " + (1.0f - A).POVRayString() + ">";
            }
        }
    }
}
